﻿#include "asm-generic/gpio.h"
#include "asm/gpio.h"
#include "linux/jiffies.h"
#include "linux/sched.h"
#include "linux/types.h"
#include <linux/module.h>
#include <linux/poll.h>

#include <linux/device.h>
#include <linux/errno.h>
#include <linux/fcntl.h>
#include <linux/fs.h>
#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/kmod.h>
#include <linux/major.h>
#include <linux/miscdevice.h>
#include <linux/mutex.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/stat.h>
#include <linux/timer.h>
#include <linux/tty.h>

struct gpio_desc {
  int gpio;
  int irq;
  char *name;
  int key;
  struct timer_list key_timer;
};

static struct gpio_desc gpios[2] = {
    {
        115,
        0,
        "harry_ds18b20",
    }, 
};

/* 主设备号                                                                 */
static int major = 0;
static struct class *gpio_class;
static spinlock_t ds18b20_spinlock;

static void ds18b20_udelay(int us) {
  u64 time = ktime_get_ns();
  while (ktime_get_ns() - time < us * 1000)
    ;
}
static int ds18b20_reset_and_wati_ack(void) 
{
  int timeout = 100;
  gpio_set_value(gpios[0].gpio, 0);
  ds18b20_udelay(480);
  gpio_direction_input(gpios[0].gpio);
  while (gpio_get_value(gpios[0].gpio) && timeout--) 
  {
    ds18b20_udelay(1);
  }
  if (timeout <= 0) /*no ack*/ 
    return -EIO;
  timeout = 300;
  while (!gpio_get_value(gpios[0].gpio) && timeout--) 
  {
    ds18b20_udelay(1);
  }
  if (timeout <= 0 ) /*hardware err,low_time not in 60~240us*/
    return -EIO;
  return 0;
}

static void ds18b20_send_cmd(unsigned char cmd) {
  int i;
  gpio_direction_output(gpios[0].gpio, 1);
  for (i = 0; i < 8; i++) {
    if (cmd & (1 << i)) /*Start from the low position*/
    {
      /*send 1*/
      gpio_set_value(gpios[0].gpio, 0);
      ds18b20_udelay(2);
      gpio_set_value(gpios[0].gpio, 1);
      ds18b20_udelay(60);
    } else {
      /*send 0*/
      gpio_set_value(gpios[0].gpio, 0);
      ds18b20_udelay(60);
      gpio_set_value(gpios[0].gpio, 1);
    }
  }
}
static void ds18b20_read_data(unsigned char *buf) {
  int c_bit;
  int val;
  unsigned char byte_data;
  gpio_direction_output(gpios[0].gpio, 1);
  byte_data = 0;
  for (c_bit = 0; c_bit < 8; c_bit++) {
    /*read init level*/
    gpio_direction_output(gpios[0].gpio, 0);
    ds18b20_udelay(2);
    gpio_direction_input(gpios[0].gpio);
    ds18b20_udelay(10);
    val = gpio_get_value(gpios[0].gpio);
    if (val) {
      byte_data |= (1 << c_bit);
    }
    ds18b20_udelay(15);
    gpio_direction_output(gpios[0].gpio, 1);
  }
  buf[0] = byte_data;
}

static unsigned char calcrc_1byte(unsigned char abyte) 
{
	unsigned char i, crc_1byte;
	crc_1byte = 0; //设定crc_1byte初值为0
	for (i = 0; i < 8; i++) {
	if (((crc_1byte ^ abyte) & 0x01)) 
	{
		crc_1byte ^= 0x18;
		crc_1byte >>= 1;
		crc_1byte |= 0x80;
	} 
	else
		crc_1byte >>= 1;
	abyte >>= 1;
	}
	return crc_1byte;
}
unsigned char calcrc_bytes(unsigned char *p, unsigned char len) 
{
	unsigned char crc = 0;
	while (len--) // len为总共要校验的字节数
	{
		crc = calcrc_1byte(crc ^ *p++);
	}
	return crc; //若最终返回的crc为0，则数据传输正确
}
/*https://www.cnblogs.com/yuanguanghui/p/12737740.html*/
static int ds18b20_verify_crc(unsigned char *buf) {
	uint8_t crc = 0;
	crc = calcrc_bytes(buf,8);
	if (crc == buf[8])
		return 0;
	else
		return -1;
}
static void ds18b20_calc_val(unsigned char *raw_data, int *result_data) {
  unsigned char tempL = 0, tempH = 0;
  unsigned int integer;
  unsigned char decimal1, decimal2, decimal;

  tempL = raw_data[0]; //读温度低8位
  tempH = raw_data[1]; //读温度高8位

  if (tempH > 0x7f) //最高位为1时温度是负
  {
    tempL = ~tempL; //补码转换，取反加一
    tempH = ~tempH + 1;
    integer = tempL / 16 + tempH * 16;         //整数部分
    decimal1 = (tempL & 0x0f) * 10 / 16;       //小数第一位
    decimal2 = (tempL & 0x0f) * 100 / 16 % 10; //小数第二位
    decimal = decimal1 * 10 + decimal2;        //小数两位
  } else {
    integer = tempL / 16 + tempH * 16;         //整数部分
    decimal1 = (tempL & 0x0f) * 10 / 16;       //小数第一位
    decimal2 = (tempL & 0x0f) * 100 / 16 % 10; //小数第二位
    decimal = decimal1 * 10 + decimal2;        //小数两位
  }
  result_data[0] = integer;
  result_data[1] = decimal;
}
/* 实现对应的open/read/write等函数，填入file_operations结构体 */
static ssize_t ds18b20_read(struct file *file, char __user *buf, size_t size,
                            loff_t *offset) {
  static unsigned long flags;
  int err;
  unsigned char kern_buf[9];
  int i;
  int result_buf[2];

  if (size != 8)
    return -EINVAL;
  /*start the temperature transfer*/
  spin_lock_irqsave(&ds18b20_spinlock, flags);
  err = ds18b20_reset_and_wati_ack();
  if (err) {
    printk("ds18b20_reset_and_wati_ack err one");
    spin_unlock_irqrestore(&ds18b20_spinlock, flags);
    return err;
  }
  ds18b20_send_cmd(0xcc);
  ds18b20_send_cmd(0x44);
  spin_unlock_irqrestore(&ds18b20_spinlock, flags);

  /*wait the transfer time for 1s*/
  set_current_state(TASK_INTERRUPTIBLE);
  schedule_timeout(msecs_to_jiffies(1000));

  /*read the temperature*/
  spin_lock_irqsave(&ds18b20_spinlock, flags);
  err = ds18b20_reset_and_wati_ack();
  if (err) {
    printk("ds18b20_reset_and_wati_ack err two");
    spin_unlock_irqrestore(&ds18b20_spinlock, flags);
    return err;
  }
  ds18b20_send_cmd(0xcc);
  ds18b20_send_cmd(0xbe);
  for (i = 0; i < 9; i++)
    ds18b20_read_data(&kern_buf[i]);
  spin_unlock_irqrestore(&ds18b20_spinlock, flags);
  err = ds18b20_verify_crc(kern_buf);
  if (err) {
    printk("ds18b20_verify_crc err");
    return err;
  }
  ds18b20_calc_val(kern_buf, result_buf);

  err = copy_to_user(buf, result_buf, 8);
  return 8;
}

/* 定义自己的file_operations结构体 */
static struct file_operations gpio_key_drv = {
    .owner = THIS_MODULE,
    .read = ds18b20_read,
};

/* 在入口函数 */
static int __init ds18b20_init(void) {
  int err;
  int i;
  int count = sizeof(gpios) / sizeof(gpios[0]);
  printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

  spin_lock_init(&ds18b20_spinlock);

  for (i = 0; i < count; i++) {
    err = gpio_request(gpios[i].gpio, gpios[i].name);
    gpio_direction_output(gpios[i].gpio, 1);
  }

  /* 注册file_operations 	*/
  major =
      register_chrdev(0, "harry_ds18b20", &gpio_key_drv); /* /dev/gpio_desc */
 
  gpio_class = class_create(THIS_MODULE, "harry_ds18b20_class");
  if (IS_ERR(gpio_class)) {
    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
    unregister_chrdev(major, "harry_ds18b20");
    return PTR_ERR(gpio_class);
  }

  device_create(gpio_class, NULL, MKDEV(major, 0), NULL,
                "harry_ds18b20"); /* /dev/harry_ds18b20 */

  return 0;
}

/* 有入口函数就应该有出口函数：卸载驱动程序时，就会去调用这个出口函数
 */
static void __exit ds18b20_exit(void) {
  int i;
  int count = sizeof(gpios) / sizeof(gpios[0]);

  printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

  device_destroy(gpio_class, MKDEV(major, 0));
  class_destroy(gpio_class);
  unregister_chrdev(major, "harry_ds18b20");

  for (i = 0; i < count; i++) {
    gpio_free(gpios[i].gpio);
  }
}

/* 7. 其他完善：提供设备信息，自动创建设备节点 */

module_init(ds18b20_init);
module_exit(ds18b20_exit);

MODULE_LICENSE("GPL");